#include <string.h>//C的头文件
#include <iostream>
#include <string>//C++的头文件

using std::cout;
using std::endl;
using std::string;
           //将参数类型推导出来
//函数模板 ======================模板函数
//          将参数类型实例化
//实例化：显示实例化和隐式实例化
//
//模板
template <typename T>//模板参数列表, T类型参数
T add(T x, T y)
{
    cout << "T add(T, T)" << endl;
    return x + y;
}

template <typename T>//模板参数列表, T类型参数
T add(T x, T y, T z)
{
    cout << "T add(T, T, T)" << endl;
    return x + y + z;
}

template <typename T1, typename T2>
T1 add(T1 x, T2 y)
{
    cout << "T3 add(T1, T2)" << endl;
    return x + y;
}

//1、普通函数可以与函数模板进行重载
//2、普通函数是优于函数模板执行的
//3、函数模板可以与函数模板进行重载
//
#if 0
int add(int x, int y)//普通函数
{
    cout << "int add(int, int)" << endl;
    return x + y;
}
#endif

#if 0
int add(int x, int y)
{
    return x + y;
}

char add(char x, char y)
{
    return x + y;
}

double add(double x, double y)
{
    return x + y;
}

string add(string x, string y)
{
    return x + y;
}
#endif

//模板的特化
//分类：模板的全特化和模板的偏特化(部分特化)
template <>
const char *add<const char *>(const char *px1, const char *px2)
{
    cout << "const char *add(const char *, const char *)" << endl;
    //获取px1与px2数据的程度
    size_t len1 = strlen(px1);
    size_t len2 = strlen(px2);
    size_t len = len1 + len2 + 1;
    //两种方法1、栈 2、堆
    /* char str[len];//ok */
    char *ptmp = new char[len]();//ok
    strcpy(ptmp, px1);
    strcat(ptmp, px2);

    return ptmp;
}

void test()
{
    int ia = 3, ib = 4, ic = 5;
    char ca = 'a', cb = 1;
    double da = 3.3, db = 4.4;
    string s1 = "hello";
    string s2 = "world";
    const char *pstr1 = "hello";
    const char *pstr2 = "world";

    cout << "add(ia, ib) = " << add(ia, ib) << endl;//隐式实例化
                    //显示实例化,就是将模板参数显示写出来
    cout << "add(ca, cb) = " << add<char>(ca, cb) << endl;
    cout << "add(da, db) = " << add<double>(da, db) << endl;
    cout << "add(s1, s2) = " << add(s1, s2) << endl;
    cout << "add(ia, ib, ic) = " << add(ia, ib, ic) << endl;//隐式实例化

    //从ia角度看T会推导成int，从db角度看T会推导成double
    //所以此处T就会产生二义性
    //模板的类型推导是比较严格的
    cout << "add(ia, db) = " << add(ia, db) << endl;
    cout << "add(pstr1, pstr2) = " << add(pstr1, pstr2) << endl;
}

int main(int argc, char **argv)
{
    test();
    return 0;
}

